Una de las premisas de Python era que "La legibilidad cuenta", y el uso de funciones ayudan mucho en que un código sea legible.
En Python no existen los procedimientos: son todas funciones. Incluso, aunque nosotros no devolvamos ningún valor, Python lo hará por nosotros, retornando None
.
La forma de devolver valores es, al igual que en C, usando la palabra reservada return y el valor a retornar. Y de igual forma, una vez que se ejecuta esa sentencia, no se ejecuta ninguna sentencia más de esa función; sin importar si está dentro de un ciclo o todavía no hayamos hecho nada.
La definición de una función comienza usando la palabra reservada def, y continúa dejando un espacio, poniendo el nombre de la función, los parámetros entre paréntesis(los paréntesis son obligatorios por más que no se pasen parámetros) y un dos puntos para terminar la línea. En las líneas que le siguen va el código de la función, que, al igual que para las estructuras de control, la forma en que se indica el bloque de código que se tiene que ejecutar es haciendo uso de la indentación.
El nombre de la función tiene que cumplir las mismas reglas para las variables, puede empezar con cualquier letra y el _ y después le puede seguir cualquier carácter alfanumérico más el _.
Por ejemplo:
In [1]:
def sumar(x, y): # Defino la función sumar
suma = x + y
return suma
x = 4
z = 5
print sumar(x, z) # Invoco a la función sumar con los
# parámetros x y z
print sumar(1, 2) # Invoco a la función sumar con los
# parámetros 1 y 2
Aunque en ningún momento indicamos que lo que tiene que sumar son números, por lo que también puede sumar strings:
In [2]:
print sumar('hola ', 'mundo')
Además, a esta función le podría agregar comentarios (docstrings) para que al hacer help de la función se entienda qué es lo que hace:
In [3]:
def sumar(x, y):
"""Suma dos elementos y retorna el resultado.
"""
return x + y
help(sumar)
El resultado de la función no es necesario que lo guarde en una variable, tranquilamente la puedo invocar y perder ese valor.
In [4]:
def factorial(n):
"""Calcula el factorial de un número de forma iterativa.
"""
for i in range(1,n):
n *= i
return n
fact_5 = factorial(5) # calculo el factorial de 5 y lo guardo
# en fact_5
factorial(10) # calculo el factorial de 10 y no lo guardo en
# ninguna variable
Out[4]:
¿Y qué sucede si no pongo el return en una función?
In [5]:
def imprimir(msg):
print msg
imprimir('Hola mundo')
¿Y si le asigno el resultado de este procedimiento a una variable?
In [6]:
resultado = imprimir('Hola mundo')
print resultado
Por lo que no existen los procedimientos, los "procedimientos" en realidad son funciones que devuelven None. Y una prueba más de esto es el resultado de llamar a la función type y pasarle como parámetro la función sumar y el "procedimiento" imprimir:
In [7]:
print type(imprimir)
print type(sumar)
print sumar
Ahora, si la función es un tipo de dato, significa que se lo puedo asignar a una variable...
In [8]:
mi_suma = sumar
¿Y qué pasa si ahora llamo a mi_suma con los parámetros 1 y 2 como hice antes con sumar?
In [9]:
print mi_suma(1, 2)
print id(mi_suma)
print id(sumar)
¿Y cómo podemos hacer si queremos devolver dos variables en lugar de una?.
Una opción simple sería retornar una lista o una tupla con todos las variables, de esa forma nos podría quedar:
def suma_y_resta(x, y):
"""Función que suma y resta dos números."""
resultado = []
resultado.append(x+y)
resultado.append(x-y)
return resultado
Despues cuando querramos usarla sólo tendríamos que hacer:
resultado = suma_y_resta(23, 5)
suma = resultado[0]
resta = resultado[1]
Pero así como podemos construir la lista y agregarle los valores, tranquilamente podríamos consutruirla directamente con los valores que queremos que tenga:
def suma_y_resta(x, y):
"""Función que suma y resta dos números."""
resultado = [x+y, x-y]
return resultado
Y si decíamos que la única diferencia entre una lista y una tupla era que la primera se podía modificar y la segunda no, entonces, tranquilamente podríamos reemplazar la lista por una tupla y hasta obviar la variable resultado y directamente:
def suma_y_resta(x, y):
"""Función que suma y resta dos números."""
return (x+y, x-y)
Incluso, los paréntesis son opcionales para crear una tupla:
In [10]:
tupla = 1,
print tupla
print type(tupla)
Entonces, nos podría quedar:
def suma_y_resta(x, y):
"""Función que suma y resta dos números."""
return x+y, x-y
Y si vamos un poco más allá, el unpacking de una lista o tupla se puede hacer en una sóla instrucción
In [11]:
x, y, z = [1, [2, 3, 4, 5], 3]
print x
print y
print z
Por lo que también podemos cambiar la forma en que se desempacan esos valores que retorna la función y nos podría quedar:
In [12]:
def suma_y_resta(x, y):
"""Función que suma y resta dos números."""
return x+y, x-y
suma, resta = suma_y_resta(23, 5)
print 'La suma es: ', suma
print 'La resta es: ', resta
A diferencia de otros lenguajes, en Python todos los parámetros que se le pasan a una función son siempre por referencia, es decir, cualquier modificación que sufran dentro de la función, también se verá reflejada fuera de la misma.
Sin embargo, cuando le pasamos un parámetro inmutable, como puede ser un int, bool o una tupla, si el parámetro formal se modifica, el parámetro actual no verá esa moficación ya que en realidad se modificó otra posición de memoria:
In [13]:
def multiplicar_y_agregar(num, lista):
num *= 2
lista.append(num)
print 'Dentro de la función num vale {num} y ' \
'lista vale {lista}'.format(num=num, lista=lista)
lista = []
n = 2
print '1. Al comenzar num vale {num} y lista ' \
'vale {lista}'.format(num=n, lista=lista)
multiplicar_y_agregar(n, lista)
print '2. Fuera de la función num vale {num} y ' \
'lista vale {lista}'.format(num=n, lista=lista)
print
n *= 3
print 'Ahora multiplico por 3: n *= 3 --> n = {}'.format(n)
multiplicar_y_agregar(n, lista)
print '3. Fuera de la función num vale {num} y lista ' \
'vale {lista}'.format(num=n, lista=lista)
¿Qué pasa cuando no sabemos cuántos parámetros nos pueden pasar, pero si sabemos qué hacer con ellos?
In [14]:
def sumar(*lista_de_numeros):
suma = 0
for e in lista_de_numeros:
suma += e
return suma
print sumar(1, 2)
print sumar(1, 2, 3, 4, 5)
print sumar(*[1, 2, 3, 4, 5, 6])
print sumar
Algo más común que no saber la cantidad de parámetros que nos van a pasar es asumir que ciertos parámetros pueden no pasarlos y para ellos asumiremos un valor por defecto.
Por ejemplo:
In [15]:
def imprimir_parametros(param1, param2, param3=5,
param4="es el cuarto parametro",
param5=False):
print param1, param2, param3, param4, param5
Para esta función nos pueden pasar 2, 3, 4 o 5 parámetros. Si nos pasan los 5 parámetros, se imprimirán los valores que nos pasen:
In [16]:
imprimir_parametros(1, 2, 3, 4, 5)
Ahora, si nos pasan 4 parámetros, el intérprete asumirá que el faltante es param5, por lo que dicho parámetro tomará el valor False. Y lo mismo pasa con el resto de los parámetros.
In [17]:
imprimir_parametros(1, 2, 3, 4)
imprimir_parametros(1, 2, 3)
imprimir_parametros(1, 2)
¿Y si le pasamos un sólo parámetro?.
In [18]:
imprimir_parametros(1)
¿Y qué pasa si quiero pasarle los parámetros 1, 2 y el 5?.
No es problema, para eso tenemos que usar parámetros nombrados:
In [19]:
imprimir_parametros(1, 2, param5="Este el parametro5")
imprimir_parametros(1, 2)
Lo mismo pasa si lo que quiero cambiar es el cuatro parámetro:
In [20]:
imprimir_parametros(1, 2, param4=4)
Hasta se pueden nombrar todos los parámetros:
In [21]:
imprimir_parametros(param5=1, param3=2, param1=3, param2=4, param4=5)
Si bien puede parecer innecesario el uso de parámetros nombrados, en algunas oportunidades se suele usar para agregar claridad y legibilidad al código, y en otros para pasarle un diccionario:
In [22]:
parametros = {
'param1': 1,
'param2': 2,
'param3': 3,
'param4': 4,
'param5': 5,
}
imprimir_parametros(**parametros)
Así como en Pascal usando la cláusula Uses
podíamos usar código que no pertenecía al archivo que estábamos codificando, en Python podemos hacer lo mismo usando la cláusula import y poniendo a continuación el nombre del módulo.
Por ejemplo, si queremos importar el módulo datetime para trabajar con fechas y horas, tendríamos que hacer:
import datetime
Para usarlo simplemente tenemos que poner el nombre del módulo, un punto y la función que queramos usar.
En este caso, dentro del módulo datetime
vamos a usar la función que se encuentra en date
y se llama today()
.
In [23]:
import datetime
print datetime.date.today()
Pero a diferencia de Pascal y C, acá podemos elegir importar una función o algo en particular de ese módulo, en lugar de traerlo todo. Para eso tendríamos que poner en primer lugar la cláusula from
, luego el nombre del módulo y a continuación la cláusula import
todo lo que queremos importar separada por comas.
Por ejemplo, del módulo datetime
podríamos traer los submódulos date
y time
. Después, para usarlos simplemente lo hacemos llamando lo que importamos sin el nombre del módulo.
In [24]:
from datetime import date, time
print date.today()
print time(1, 23, 32)
Si tenemos un archivo llamado ejemplo.py que tiene el siguiente código:
def imprimir(param):
print param
def sumar(n1, n2):
return n1+n2
y queremos importarlo a otro archivo y usarlo, podemos hacer:
import ejemplo
ejemplo.imprimir("123")
print ejemplo.sumar(2,3)
Y, como dijimos, también podemos importar solo una función de ese módulo y usarla como si estuviera definida dentro de nuestro contexto.
from ejemplo import sumar
print sumar(4, 5)